/// \file
/// \brief Not code so much as a specification for the structures and classes that an implementation of the lobby server can use.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.

#ifndef __LOBBY_SERVER_DATABASE_INTERFACE_H
#define __LOBBY_SERVER_DATABASE_INTERFACE_H

#include "Export.h"
#include "DS_List.h"
#include <string>
#include "RefCountedObj.h"

namespace RakNet
{
	class BitStream;
}

// See LobbyDB_PostgreSQL::CreateLobbyServerTables in LobbyDB_PostgreSQL.cpp for CREATE TABLE table specification

namespace LobbyDBSpec
{
	// --------------------- TYPES ---------------------------
	/// Type of the primary keys in the database
	typedef unsigned DatabaseKey;

	struct PairedKeyDbId
	{
		DatabaseKey primaryKey, secondaryKey;
	};


	struct UserIdOrHandle
	{
		/// [in] Use the variable userIdFrom? Faster if true. Otherwise it will lookup the ID using userHandleFrom.
		bool hasUserId;
		/// [in] The database primary key of the user.
		DatabaseKey userId;
		/// [in] If you don't know the user primary key, look it up by this handle.
		std::string userHandle;
	};

	// --------------------- (IMPLEMENT THESE FOR A PARTICULAR DB) ---------------------------

	/// Adds a user to the users table.
	class CreateUser_Data : public RefCountedObj
	{
	public:
		CreateUser_Data();
		virtual ~CreateUser_Data();
		void Serialize(RakNet::BitStream *bs); // Does NOT serialize UserIdOrHandle
		bool Deserialize(RakNet::BitStream *bs);
		void SerializeCCInfo(RakNet::BitStream *bs);
		void SerializeBinaryData(RakNet::BitStream *bs);
		void SerializePersonalInfo(RakNet::BitStream *bs);
		void SerializeEmailAddr(RakNet::BitStream *bs);
		void SerializePassword(RakNet::BitStream *bs);
		void SerializeCaptions(RakNet::BitStream *bs);
		void SerializeOtherInfo(RakNet::BitStream *bs);
		void SerializePermissions(RakNet::BitStream *bs);
		void SerializeAccountNumber(RakNet::BitStream *bs);
		void SerializeAdminLevel(RakNet::BitStream *bs);
		void SerializeAccountBalance(RakNet::BitStream *bs);
		bool DeserializeCCInfo(RakNet::BitStream *bs);
		bool DeserializeBinaryData(RakNet::BitStream *bs);
		bool DeserializePersonalInfo(RakNet::BitStream *bs);
		bool DeserializeEmailAddr(RakNet::BitStream *bs);
		bool DeserializePassword(RakNet::BitStream *bs);
		bool DeserializeCaptions(RakNet::BitStream *bs);
		bool DeserializeOtherInfo(RakNet::BitStream *bs);
		bool DeserializePermissions(RakNet::BitStream *bs);
		bool DeserializeAccountNumber(RakNet::BitStream *bs);
		bool DeserializeAdminLevel(RakNet::BitStream *bs);
		bool DeserializeAccountBalance(RakNet::BitStream *bs);

		/// [in] Identifier for the user, cannot exist in the table disallowedHandles
		std::string handle;
		/// [in] Self-apparent
		std::string firstName;
		/// [in] Self-apparent
		std::string middleName;
		/// [in] Self-apparent
		std::string lastName;
		/// [in] Self-apparent
		std::string race;
		/// [in] Self-apparent
		std::string sex;
		/// [in] Self-apparent
		std::string homeAddress1;
		/// [in] Self-apparent
		std::string homeAddress2;
		/// [in] Self-apparent
		std::string homeCity;
		/// [in] Self-apparent
		std::string homeState;
		/// [in] Self-apparent
		std::string homeProvince;
		/// [in] Self-apparent
		std::string homeCountry;
		/// [in] Self-apparent
		std::string homeZipCode;
		/// [in] Self-apparent
		std::string billingAddress1;
		/// [in] Self-apparent
		std::string billingAddress2;
		/// [in] Self-apparent
		std::string billingCity;
		/// [in] Self-apparent
		std::string billingState;
		/// [in] Self-apparent
		std::string billingProvince;
		/// [in] Self-apparent
		std::string billingCountry;
		/// [in] Self-apparent
		std::string billingZipCode;
		/// [in] Self-apparent
		std::string emailAddress;
		/// [in] Self-apparent
		std::string password;
		/// [in] If the user needs to retrieve their password; you could ask them this question.
		std::string passwordRecoveryQuestion;
		/// [in] If the user needs to retrieve their password; you could use this for the answer.
		std::string passwordRecoveryAnswer;
		/// [in] Lobbies often allow users to set a text description of their user in some fashion.
		std::string caption1;
		/// [in] Lobbies often allow users to set a text description of their user in some fashion.
		std::string caption2;
		/// [in] Lobbies often allow users to set a text description of their user in some fashion.
		std::string caption3;
		/// [in] Self-apparent
		std::string dateOfBirth;
		/// [in] If you want to have some kind of unique account number; it can be stored here.
		DatabaseKey accountNumber;
		/// [in] In case you take credit cards
		std::string creditCardNumber;
		/// [in] In case you take credit cards
		std::string creditCardExpiration;
		/// [in] In case you take credit cards
		std::string creditCardCVV;
		/// [in] If your lobby supports moderators; you can indicate what moderator level they have here
		std::string adminLevel;
		/// [in] Special account permissions; such as being able to chat; join adult games; etc. Encode using text however you wish.
		/// TODO - Per-column read/write/update permissions for self or others.
		std::string permissions;
		/// [in] If users can associate money with their account; the current balance can be stored here.
		float accountBalance;
		/// [in] In case you want to log IPs
		std::string sourceIPAddress;
		/// [in] If your lobby supports binary data (such as user images) it can be stored with this field.
		char *binaryData;
		/// [in] If your lobby supports binary data (such as user images) the length can be in this field.
		int binaryDataLength;
	};

	/// Get a user from the users table, using userId or userHandle for the lookup
	class GetUser_Data : public RefCountedObj
	{
	public:
		/// [in] Get credit card info? (including billing info)
		bool getCCInfo;

		/// [in] Get binary data?
		bool getBinaryData;

		/// [in] Get personal information (Name, address)
		bool getPersonalInfo;

		/// [in] Get email address?
		bool getEmailAddr;

		/// [in] Get password, recovery question, and recovery answer?
		bool getPassword;

		/// [in / out] Which user. If hasUserId==false userId is filled in on return.
		UserIdOrHandle id;

		/// [out] Does this user exist?
		bool userFound;

		/// [out] All [in] parameters are now [out] parameters. Data not downloaded is not changed.
		CreateUser_Data output;

		/// [out] Tag if this user is banned or not. Set directly in the DB (or I can add a function later).
		bool isBanned;

		/// [out] Tag if this user is suspended or not. Suspensions are temporary
		bool isSuspended;

		/// [out] When the suspension expires. Parse with _localtime64()
		long long suspensionExpiration;

		/// [out] Why the user was banned or suspended.
		std::string banOrSuspensionReason;

		/// [out] When this row was added to the database. Parse with _localtime64()
		long long creationTime;
	};

	/// Overwrites an existing user in the users table
	class UpdateUser_Data : public RefCountedObj
	{
	public:
		void Serialize(RakNet::BitStream *bs);  // Does NOT serialize UserIdOrHandle
		bool Deserialize(RakNet::BitStream *bs); // =&input
		bool Deserialize(RakNet::BitStream *bs, CreateUser_Data *output, bool deserializeBooleans);
		void SerializeBooleans(RakNet::BitStream *bs);
		bool DeserializeBooleans(RakNet::BitStream *bs);

		/// [in] Get credit card info?
		bool updateCCInfo;

		/// [in] Get binary data?
		bool updateBinaryData;

		/// [in] Get personal information (Name, address)
		bool updatePersonalInfo;

		/// [in] Get email address?
		bool updateEmailAddr;

		/// [in] Get password, recovery question, and recovery answer?
		bool updatePassword;

		// [in]
		bool updateCaptions;

		// [in]
		bool updateOtherInfo;

		// [in]
		bool updatePermissions;

		// [in]
		bool updateAccountNumber;

		// [in]
		bool updateAdminLevel;

		// [in]
		bool updateAccountBalance;

		/// [in] Which user
		UserIdOrHandle id;

		/// [in] Will overwrite what is there already, based on the update booleans in this structure.
		/// Will fail if the new handle is different and already in use.
		CreateUser_Data input;
	};

	class DeleteUser_Data : public RefCountedObj
	{
	public:
		/// [in] Which user
		UserIdOrHandle id;
	};

	class ChangeUserHandle_Data : public RefCountedObj
	{
	public:
		/// [in] Which user
		UserIdOrHandle id;

		/// [in] New handle to change to
		std::string newHandle;

		/// [out] If the query succeeded
		bool success;
		/// [out] Description of the query result.
		std::string queryResult;
	};

	/// Adds an entry to the accountNotes table for an existing user
	class AddAccountNote_Data : public RefCountedObj
	{
	public:
		/// [in] Which user
		UserIdOrHandle id;

		/// [in] Write the variable moderatorId?
		bool writeModeratorId;
		/// [in] internal ID of the moderator (does not have to be in the users table).
		DatabaseKey moderatorId;
		/// [in] internal username of the moderator (does not have to be in the users table)
		std::string moderatorUsername;
		/// [in] Type of account note (optional)
		std::string type;
		/// [in] Subject of the account note (optional)
		std::string subject;
		/// [in] Body of the account note (optional)
		std::string body;
		/// [out] When this row was added to the database. Used by GetAccountNotes_Data only.
		long long time;
		/// [out] If the user was found in the database
		bool userFound;
	};

	/// Gets all account notes for an existing user
	class GetAccountNotes_Data : public RefCountedObj
	{
	public:

		virtual ~GetAccountNotes_Data();

		/// [in / out] Which user.
		UserIdOrHandle id;

		/// [in] Sort list by creationTime, ascending. False for descending.
		bool ascendingSort;
		// All account notes for this user. Sorted by creationTime. AddAccountNote_Data [in] parameters are now [out] parameters. AddAccountNote_Data::userFound and writeModeratorId is not used.
		DataStructures::List<AddAccountNote_Data*> accountNotes;
	};

	/// Adds a userId / friendId to the friends table
	class AddFriend_Data : public RefCountedObj
	{
	public:

		/// [in] Which user
		UserIdOrHandle id;

		/// [in] Which friend
		UserIdOrHandle friendId;

		/// [out] When this row was added to the database. Used by GetFriends_Data only.
		long long creationTime;

		/// [out] If the user was found in the database
		bool userFound;
		/// [out] If the friend was found in the database
		bool friendFound;
		/// [out] If the query succeeded
		bool success;
		/// [out] Description of the query result.
		std::string queryResult;
	};

	/// Removes a userId / friendId from the friends table
	class RemoveFriend_Data : public RefCountedObj
	{
	public:
		/// [in] Same fields as AddFriend_Data, but the operation performed is different.
		/// \note Bad input, such as removing someone that is not your friend, simply does nothing
		AddFriend_Data removeFriendInput;
	};

	/// Gets the friends table for a userId or userHandle
	class GetFriends_Data : public RefCountedObj
	{
	public:

		virtual ~GetFriends_Data();

		/// [in / out] Which user. If hasUserId==false userId is filled in.
		UserIdOrHandle id;

		/// [in] Sort list by creationTime, ascending. False for descending.
		bool ascendingSort;
		// All friends for this user. AddFriend_Data [in] parameters are now [out] parameters.
		DataStructures::List<AddFriend_Data*> friends;
	};

	/// Adds to the ignoreList table for a particular userId / userHandle
	class AddToIgnoreList_Data : public RefCountedObj
	{
	public:
		/// [in] Which user
		UserIdOrHandle id;

		// Which user to ignore
		UserIdOrHandle ignoredUser;

		/// [in] If you want to specify particular actions to ignore, you can encode them into this string.
		/// [in] If this user is already in the ignore list, it will update actions
		std::string actions;

		/// [out] If the query succeeded
		bool success;

		/// [out] Description of the query result.
		std::string queryResult;

		/// [out] When this row was added to the database. Used by GetIgnoreList_Data only.
		long long creationTime;
	};

	/// Removes from the ignoreList table for a particular userId / userHandle
	class RemoveFromIgnoreList_Data : public RefCountedObj
	{
	public:
		/// [in] Which user
		UserIdOrHandle id;

		// Which user to no longer ignore
		UserIdOrHandle ignoredUser;
	};

	/// Gets the ignoreList table for a particular userId / userHandle
	class GetIgnoreList_Data : public RefCountedObj
	{
	public:

		virtual ~GetIgnoreList_Data();

		/// [in / out] Which user. If hasUserId==false userId is filled in.
		UserIdOrHandle id;

		/// [in] Sort list by creationTime, ascending. False for descending.
		bool ascendingSort;
		// All ignored (enemies) for this user. AddToIgnoreList_Data [in] parameters are now [out] parameters.
		DataStructures::List<AddToIgnoreList_Data*> ignoredUsers;
	};

	/// Adds an email to the emails table. Added once for the user with inbox=false, n times for n recipients with inbox=true
	class SendEmail_Data : public RefCountedObj
	{
	public:
		SendEmail_Data();
		virtual ~SendEmail_Data();

		void Serialize(RakNet::BitStream *bs);
		bool Deserialize(RakNet::BitStream *bs);

		/// [in] When sending, our own system
		/// [out] When calling GetEmails, the other system (the one that sent us the email, or the one we sent to)
		UserIdOrHandle id;

		/// [in] recipients, used for SendEmail
		DataStructures::List<UserIdOrHandle> to;

		/// [in] Email subject
		std::string subject;
		/// [in] Email body
		std::string body;
		/// [in] Binary data of your choosing. Set to 0, or attachmentLength to 0 to not use.
		char *attachment;
		/// [in] Length of the binary data
		int attachmentLength;
		/// [in] Use for your own status flags (marked, read, priority, etc). Update with UpdateEmailStatus
		int initialSenderStatus, initialRecipientStatus;
		/// [out] Current status of the email, Used by GetEmails_Data
		int status;
		/// [in/out] For sending emails, initially mark this email as opened?
		/// For receiving mails, can be set by UpdateEmailStatus_Data
		/// Also returned by by GetEmails_Data
		bool wasOpened;

		/// [out] When this row was added to the database. Used by GetEmails_Data.
		long long creationTime;
		/// [out] Email primary key. Used by GetEmails_Data.
		DatabaseKey emailMessageID;

		/// [out] Are the parameters of this email valid?
		bool validParameters;
		/// [out] If validParameters==false, this is why
		std::string failureMessage;
		
	};

	/// Gets all emails in the emails table for a particular user
	class GetEmails_Data : public RefCountedObj
	{
	public:

		virtual ~GetEmails_Data();

		/// [in / out] Which user. If hasUserId==false userId is filled in.
		UserIdOrHandle id;

		/// [in] Check the inbox or sent emails?
		bool inbox;

		/// [in] Sort list by creationTime, ascending. False for descending.
		bool ascendingSort;

		/// [out] Are the parameters of this email valid?
		bool validParameters;

		/// [out] If validParameters==false, this is why
		std::string failureMessage;

		// [out] List of emails. creationTime and emailMessageID will be filled in for each email.
		DataStructures::List<SendEmail_Data*> emails;
	};


	/// Deletes an email from either the incomingEmails or sentEmails table
	class DeleteEmail_Data : public RefCountedObj
	{
	public:

		/// [in] Refers to what email to delete. Get from GetEmails_Data structure, member emails::emailMessageID
		DatabaseKey emailMessageID;
	};

	/// Updates the mark flags on an email
	class UpdateEmailStatus_Data : public RefCountedObj
	{
	public:
		/// [in] Status flags, define as you wish
		int status;

		/// [in] Update the opened status flag on the email
		bool wasOpened;

		/// [in] Refers to what email to mark as read.
		DatabaseKey emailMessageID;
	};

	class GetHandleFromUserId_Data : public RefCountedObj
	{
	public:

		/// [in] The database primary key of the user.
		DatabaseKey userId;
		/// [out] handle of this user
		std::string handle;
		/// [out] success
		bool success; // Was the lookup successful?
	};

	class GetUserIdFromHandle_Data : public RefCountedObj
	{
	public:

		/// [in] handle of this user
		std::string handle;
		/// [out] The database primary key of the user.
		DatabaseKey userId;
		/// [out] success
		bool success; // Was the lookup successful?
	};

	class AddDisallowedHandle_Data : public RefCountedObj
	{
	public:

		/// [in] Handle to add to the table disallowedHandles
		std::string handle;
	};

	class RemoveDisallowedHandle_Data : public RefCountedObj
	{
	public:

		/// [in] Handle to remove from the table disallowedHandles
		std::string handle;
	};

	class IsDisallowedHandle_Data : public RefCountedObj
	{
	public:

		/// [in] Handle to check
		std::string handle;
		/// [out] Is this handle in the table disallowedHandles?
		bool exists;
	};

	class AddToActionHistory_Data : public RefCountedObj
	{
	public:
		void Serialize(RakNet::BitStream *bs);
		bool Deserialize(RakNet::BitStream *bs);

		/// [in] Which user
		UserIdOrHandle id;
		/// [in] When this action occurred (if you want something in addition to creationTime)
		std::string actionTime;
		/// [in] What action was taken
		std::string actionTaken;
		/// [in] Description of the action
		std::string description;
		/// [in] IP Address this action occurred from
		std::string sourceIpAddress;
		/// [out] When this row was added to the database. Used by GetUserHistory_Data and filled out when inserted.
		long long creationTime;
	};

	class GetActionHistory_Data : public RefCountedObj
	{
	public:

		void Serialize(RakNet::BitStream *bs); // Does NOT serialize UserIdOrHandle
		bool Deserialize(RakNet::BitStream *bs);

		virtual ~GetActionHistory_Data();

		/// [in / out] Which user. If hasUserId==false userId is filled in.
		UserIdOrHandle id;

		/// [in] Sort list by creationTime, ascending. False for descending.
		bool ascendingSort;
		// [out] History for this user. Sorted by creationTime::AddToUserHistory_Data
		DataStructures::List<AddToActionHistory_Data*> history;
	};
}


#endif
